package com.gitee.zycra.union.api.service;

import com.gitee.zycra.jdbc.model.PageResult;
import com.gitee.zycra.union.api.common.bo.CommonResult;
import com.gitee.zycra.union.api.common.bo.PageQuery;
import com.gitee.zycra.union.api.common.model.ApiUser;
import com.gitee.zycra.union.api.common.param.AddUserParam;
import com.gitee.zycra.union.api.common.param.PageQueryUserParam;
import com.gitee.zycra.union.api.common.param.UpdatePasswordParam;
import com.gitee.zycra.union.api.common.param.UserLoginParam;
import com.gitee.zycra.union.api.common.util.AESUtil;
import com.gitee.zycra.union.api.common.util.CaptchaUtil;
import com.gitee.zycra.union.api.common.util.DateUtil;
import com.gitee.zycra.union.api.common.util.UserInfoUtil;
import com.gitee.zycra.union.api.common.vo.UserVO;
import com.gitee.zycra.union.api.common.vo.ProjectGroupVO;
import com.gitee.zycra.union.api.common.vo.UserAuthVO;
import com.gitee.zycra.union.api.common.vo.UserLoginVO;
import com.gitee.zycra.union.api.manager.ApiProjectGroupManager;
import com.gitee.zycra.union.api.manager.ApiUserGroupManager;
import com.gitee.zycra.union.api.manager.ApiUserManager;
import lombok.extern.slf4j.Slf4j;
import org.mindrot.jbcrypt.BCrypt;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;

/**
 * @author zycra
 */
@Slf4j
@Service
public class ApiUserService {

    @Autowired
    private ApiUserManager apiUserManager;

    @Autowired
    private ApiUserGroupManager apiUserGroupManager;

    @Autowired
    private ApiProjectGroupManager apiProjectGroupManager;

    /**
     * token加密密钥
     */
    @Value("${tokenKey}")
    private String tokenKey;

    /**
     * 验证码宽度
     */
    private static final int CAPTCHA_WIDTH = 160;

    /**
     * 验证码高度
     */
    private static final int CAPTCHA_HEIGHT = 40;

    /**
     * toekn过期时间
     */
    private static final long TOKEN_EXPIRE_MINUTES = 30L;

    /**
     * 插入
     *
     * @param addUserParam 调度时间配置对象
     * @return 操作结果
     */
    public CommonResult<String> insertApiUser(AddUserParam addUserParam) {
        apiUserManager.insertApiUser(AddUserParam.convertToApiUser(addUserParam));
        return CommonResult.success();
    }

    /**
     * 禁用/启用
     *
     * @param id      用户ID
     * @param enabled 是否启用
     * @return 操作结果
     */
    public CommonResult<String> switchEnabled(Long id, Boolean enabled) {
        apiUserManager.switchEnabled(id, enabled);
        return CommonResult.success();
    }

    /**
     * 更新密码
     *
     * @param param 更新密码对象
     * @return 更新条数
     */
    public CommonResult<String> updatePassword(UpdatePasswordParam param) {
        Long userId = UserInfoUtil.getUserId();
        ApiUser apiUser = apiUserManager.selectApiUserById(userId);
        boolean success = BCrypt.checkpw(param.getOldPassword(), apiUser.getPwd());
        if (!success) {
            log.warn("旧密码错误, userId={}", userId);
            return CommonResult.failed("旧密码错误");
        }
        apiUserManager.updateApiUser(new ApiUser().setId(userId).setPwd(BCrypt.hashpw(param.getNewPassword(), BCrypt.gensalt())));
        return CommonResult.success();
    }

    /**
     * 根据条件查询
     *
     * @param pageQuery 查询条件
     * @return 查询到的数据
     */
    public PageResult<UserVO> selectApiUserForPage(PageQuery<PageQueryUserParam> pageQuery) {
        PageResult<ApiUser> result = apiUserManager.selectApiUserForPage(pageQuery);
        List<ApiUser> data = result.getData();
        List<UserVO> voList = new ArrayList<>();
        for (ApiUser apiUser : data) {
            List<UserAuthVO> authGroup = apiUserGroupManager.selectUserAuthByUserId(apiUser.getId());
            voList.add(UserVO.buildApiUserVO(apiUser, authGroup));
        }
        return new PageResult<>(result.getPage(), result.getTotal(), voList);
    }

    /**
     * 登录
     *
     * @param userLoginParam 登录参数
     * @return 登录结果对象
     */
    public CommonResult<UserLoginVO> login(UserLoginParam userLoginParam) {
        String captchaToken = userLoginParam.getCaptchaToken();
        if (captchaToken != null) {
            captchaToken = AESUtil.decrypt(captchaToken, tokenKey);
            if (captchaToken == null || !captchaToken.equalsIgnoreCase(userLoginParam.getCaptcha())) {
                log.warn("验证码校验失败, userLoginParam={}", userLoginParam);
                return getFailedVO("验证码校验失败");
            }
        }
        ApiUser apiUser = apiUserManager.selectApiUserByUsername(userLoginParam.getUsername());
        if (apiUser == null) {
            log.warn("用户不存在, userLoginParam={}", userLoginParam);
            return getFailedVO("用户不存在");
        }
        LocalDateTime lastLoginTime = apiUser.getLastLoginTime();
        LocalDateTime now = DateUtil.getNowLocalDateTime();
        apiUserManager.updateApiUser(new ApiUser().setId(apiUser.getId()).setLastLoginTime(DateUtil.getNowLocalDateTime()));
        if (captchaToken == null && lastLoginTime.isAfter(now.minusMinutes(1L))) {
            log.warn("未输入验证码, userLoginParam={}", userLoginParam);
            return getFailedVO("请输入验证码");
        }
        if (lastLoginTime.isAfter(now.minusSeconds(1L))) {
            log.warn("登录频繁, userLoginParam={}", userLoginParam);
            return getFailedVO("登录频繁,请稍后重试");
        }
        boolean success = BCrypt.checkpw(userLoginParam.getPwd(), apiUser.getPwd());
        if (!success) {
            log.warn("用户名或密码错误, userLoginParam={}", userLoginParam);
            return getFailedVO("用户名或密码错误");
        }
        Long expireTime = DateUtil.convertToTimestamp(DateUtil.getNowLocalDateTime().plusMinutes(TOKEN_EXPIRE_MINUTES));
        String superAdmin = String.valueOf(apiUser.getSuperAdmin());
        String token = AESUtil.encrypt(apiUser.getId().toString() + "," + apiUser.getUsername() + "," + superAdmin + "," + expireTime,
                tokenKey);
        return CommonResult.success("登录成功", new UserLoginVO().setToken(token));
    }

    private CommonResult<UserLoginVO> getFailedVO(String message) {
        CaptchaUtil.Captcha captcha = CaptchaUtil.createCaptcha(CAPTCHA_WIDTH, CAPTCHA_HEIGHT);
        return CommonResult.failed(message, new UserLoginVO()
                .setCaptchaToken(AESUtil.encrypt(captcha.getCaptchaCode(), tokenKey))
                .setCaptchaImage(captcha.getCaptchaImage()));
    }

    /**
     * 获取验证码
     *
     * @return 验证码
     */
    public CommonResult<UserLoginVO> getCaptcha() {
        CaptchaUtil.Captcha captcha = CaptchaUtil.createCaptcha(CAPTCHA_WIDTH, CAPTCHA_HEIGHT);
        return CommonResult.success(new UserLoginVO()
                .setCaptchaToken(AESUtil.encrypt(captcha.getCaptchaCode(), tokenKey))
                .setCaptchaImage(captcha.getCaptchaImage()));
    }

    /**
     * 查询当前用户的项目组
     *
     * @return 查询结果
     */
    public CommonResult<List<ProjectGroupVO>> userGroupList() {
        if (UserInfoUtil.getSuperAdmin()) {
            return CommonResult.success(apiProjectGroupManager.selectAllGroup());
        }
        return CommonResult.success(apiUserGroupManager.selectUserGroups(UserInfoUtil.getUserId()));
    }
}
